package asteroids.model;
/**
 * ALS EEN SHIP NAAR NOWORLD GAAT MOETEN ALLE BULLETS VERWIJDERD WORDEN!!!!!!!!!
 */

import ModelExceptions.*;
import asteroids.Util;
import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Raw;

public class Bullet extends CollisionObject{
	/**
	 * 
	 * @param xCoordinate
	 * @param yCoordinate
	 * @param xVelocity
	 * @param yVelocity
	 * @param radius
	 * @param ship
	 * @param density
	 * @throws IllegalRadiusException	...
	 * 									|!isValidRadius(radius)
	 * @throws IllegalDensityException	...
	 * 									|!isValidDensity(density)
	 * @post	If radius ,xVelocity and yVelocity are valid inputs, then there will be made an object of the class ship,
	 * 			with the given values
	 * 			|if(isValidVelocity(xVelocity,yVelocity)
	 * 			|			&& isValidRadius(radius) 
	 * 			|			&& isValidCoordinate(xCoordinate) 
	 * 			|			&& isValidCoordinate(yCoordinate)) then
	 * 			| 	new.getXCoordinate() 	== xCoordinate
	 * 			| 	new.getYCoordinate() 	== yCoordinate
	 * 			| 	new.getXVelocity() 		== xVelocity
	 * 			| 	new.getYVelocity() 		== yVelocity
	 * 			| 	new.getRadius() 		== radius
	 * 			| 	new.getAngle() 			== angle
	 * 			| 	new.getDensity() 		== density
	 * @invar	...
	 * 			|this.getBulletSource()!=null
	 * @invar 	...
	 * 			|this.canHaveAsSourceShip(getBulletSource)
	 */
	@Raw
	public Bullet(double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius,Ship ship,double density) throws OutOfBoundsException, IllegalRadiusException, IllegalDensityException {
		super(xCoordinate, yCoordinate, xVelocity, yVelocity, radius);
		if(isValidDensity(density))
			this.density = density;
		else 
			throw new IllegalDensityException("That is an invalid density");  
		setMass(radius);
		this.bulletSourceShip = ship;
		setWorld(ship.getWorld());
		ship.addAsBullet(this);


	}

	/**
	 * 
	 * @param xCoordinate
	 * @param yCoordinate
	 * @param xVelocity
	 * @param yVelocity
	 * @param radius
	 * @param ship
	 * @throws IllegalRadiusException	...
	 * 									|!isValidRadius(radius)
	 * @throws IllegalDensityException	...
	 * 									|!isValidDensity(density)
	 * @effect 	...
	 * 			| new Bullet(xCoordinate, yCoordinate, xVelocity, yVelocity,radius,ship, getStandardDensity());
	 * 
	 */
	@Raw
	public Bullet(double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius,Ship ship) throws OutOfBoundsException, IllegalRadiusException {
		super(xCoordinate, yCoordinate, xVelocity, yVelocity, radius);
		this.density = getStandardDensity();
		setMass(radius);
		this.bulletSourceShip = ship;
		setWorld(ship.getWorld());
		ship.addAsBullet(this);
	}

	@Basic
	public double getStandardBulletRadius() {
		return standardBulletRadius;
	}
	private final double standardBulletRadius = 3;


	/**
	 * 
	 * @return	...
	 * 			|if(isValidObject()  && isInWorld()) then
	 * 			|	result == true
	 */
	@Raw
	public boolean isValidBullet(){
		if(!isValidObject()  || !isInWorld())//&& getBulletSource()!= null && getBulletSource().hasAsBullet(this))
			return false;
		return true;
	}

	/**
	 *@post ...
	 *		|new.getMass() = Math.pow(radius, 3)*4/3*Math.PI*getDensity()
	 *@throws IllegalMassException	...
	 *								|!isValidMass(new.getMass())
	 */
	@Override @Basic
	public void setMass(double radius) throws IllegalMassException{
		double calcMass = Math.pow(radius, 3)*4/3*Math.PI*getDensity();
		if(isValidMass(calcMass))
			this.mass = calcMass;
		else 
			throw new IllegalMassException("The density and radius result in an invalid mass");
	}

	@Basic
	public Ship getShip(){
		return this.bulletSourceShip;
	}

	/**
	 * 
	 * @param ship	The ship you want to check
	 * @return	|if(ship.isValidObject() && ship.isInWorld()) then
	 * 			|	result == true
	 */
	public boolean canHaveAsSourceShip(Ship ship){
		if(ship.isValidObject() && ship.isInWorld())
			return true;
		return false;
	}

	@Basic
	public double getDensity(){
		return this.density;
	}

	/**
	 * 
	 * @param density 	The density you want to check
	 * @return			...
	 * 					|if(Util.fuzzyLessThanOrEqualTo(density, getMinimumDensity()) 
	 * 					|		|| !Util.fuzzyLessThanOrEqualTo(density, getMaximumDensity())
	 * 					|		|| Double.isNaN(density)) 		then
	 * 					|					result == true;
	 */
	public boolean isValidDensity(double density) {
		if(Util.fuzzyLessThanOrEqualTo(density, getMinimumDensity()) || !Util.fuzzyLessThanOrEqualTo(density, getMaximumDensity())|| Double.isNaN(density)){
			return true;
		}
		return false;
	}

	@Basic
	public double getMaximumDensity(){
		return this.maximumDensity;
	}

	@Basic
	public double getMinimumDensity(){
		return this.minimumDensity;
	}

	@Basic
	private double getStandardDensity(){
		return this.standardDensity;
	}

	private final double minimumDensity = 0;
	private final double maximumDensity = Double.POSITIVE_INFINITY;
	private final double density;
	private final double standardDensity = 7.8*Math.pow(10, 12);



	/**
	 * Return the source of this bullet.
	 */
	@Basic
	public Ship getBulletSource(){ //Niet clonen.Probleem anders bij destroyen in ship.
		return this.bulletSourceShip;
	}

	private Ship bulletSourceShip;

	/**
	 * The reaction that this ship will make with other collisionObjects
	 * @param collisionObject The other collisionobject to react with
	 * @effect  If the collisionObject is a bullet, than this bullet and the other bullet will both be destroyed.
	 * 			| if(collisionObject.isBullet()) then
	 * 			| 		this.destroy();
	 * 			|		collisionObject.destroy();
	 *		
	 *@effect	If the collisionObject is a ship and this bullet is fired by that ship, this bullet is destroyed, this ship is untouched.
	 *			|if(collisionObject.isShip() &&  this.getBulletSource() == collisionObject) then
	 *			| 	this.destroy()
	 *
	 *@effect	If the collisionObject is a ship and this bullet is not fired by that ship, this bullet is destroyed, that ship is destroyed.
	 *			|if(collisionObject.isShip() && this.getBulletSource() != collisionObject) then
	 *			| 	collisionObject.destroy();
	 *			|	this.destroy();
	 *
	 *@effect	If the collisionObject is an asteroid, this bullet will be destroyed and the asteroid will be destroyed or splitted.
	 *			| Asteroid asteroid = (Asteroid) collisionObject;
	 *			| asteroid.collisionInteractionWith(this);
	 *		
	 */
	@Override
	public void collisionReactionWith(CollisionObject collisionObject) {
		if(collisionObject.isBullet()){
			this.destroy();
			collisionObject.destroy();
		}
		if(collisionObject.isAsteroid()){
			collisionObject.destroy();
			this.destroy();
		}
		if(collisionObject.isShip()){
			if(collisionObject != this.getBulletSource() && this.getBulletSource() != null){
				this.destroy();
				collisionObject.destroy();
			}
			else{this.destroy();}
		}
	}
	/**
	 * 
	 * @post	...
	 * 			|this.destroy()
	 * 			|this.getBulletSource() == null;
	 */
	public void shipDestroy(){
		if(!this.isDestroyed())
			super.destroy();
		bulletSourceShip = null;
	}

	/**
	 * 
	 * @effect	...
	 * 			|super.destroy();
	 * @post	...
	 * 			|new(this.getBulletSource()).hasAsBullet(this) == false;
	 * @post	...
	 * 			|this.getBulletSource() == null;
	 */
	@Override
	public void destroy(){
		if(!this.isDestroyed())
			getBulletSource().removeAsBullet(this);
		bulletSourceShip = null;
		super.destroy();
	}

	/**
	 * @return 	...
	 * 			| "This is a bullet with \n" +
				| "Homeship: \t: " +getBulletSource() +"\n" + 
				| super.toString()+
				| "\n";
	 */
	@Override
	public String toString(){
		return "This is a bullet with \n" +
				"Homeship: \t: " +getBulletSource() +"\n" + 
				super.toString()+
				"\n";
	}

	@Basic
	public int getMaxNbOfBounces(){
		return this.maxNbOfBounces;
	}

	@Basic
	public void setMaxNbOfBounces(int max){
		this.maxNbOfBounces = max;
	}

	int maxNbOfBounces =  1;

	@Basic
	public int getNbOfBounces(){
		return this.nbOfBounces;
	}

	@Basic
	public void setNbOfBounces(int nbOfBounces){
		if(nbOfBounces>=0)
			this.nbOfBounces = nbOfBounces;
		else
			this.nbOfBounces = 0;
	}

	/**
	 * 
	 * @post 	...
	 * 			|(new this).getNbOfBounces == this.getNbOfBounces() + 1
	 */
	public void incrementNbOfBounces(){
		int amountOfBounces = getNbOfBounces() + 1;
		setNbOfBounces(amountOfBounces);
	}
	int nbOfBounces = 0;

	/**
	 * 	 * @return	...
	 * 			| result.getXCoordinate()	== 	this.getXCoordinate()
	 * 			| result.getYCoordinate()	== 	this.getYCoordinate()
	 * 			| result.getXVelocity()		==	this.getXVelocity()
	 * 			| result.getYVelocity()		== 	this.getYVelocity()
	 * 			| result.getRadius()		==	this.getRadius()
	 * 			| result.getDensity()		== 	this.getDensity()
	 * 			| result.getWorld()			==	this.getWorld()
	 * 			| result.getBulletSource() 	== 	this.getBulletSource()
	 */
	@Override
	public CollisionObject clone() {
		return new Bullet(getXCoordinate(), getYCoordinate(), getXVelocity(), getYVelocity(), getRadius(), getBulletSource(), getDensity());
	}
	
	/**
	 * 
	 * @return	...
	 * 			|result == getNbOfBounces()>=getMaxNbOfBounces()
	 */
	public boolean checkNbOfBounces(){
		if(this.getNbOfBounces() >= this.getMaxNbOfBounces())
			return true;
		return false;
	}
	
	/**
	 * 
	 * @effect	...
	 * 			|if(checkNbOfBounces) then
	 * 			|	destroy;
	 * @effect	...
	 * 			|if(!checkNubOfBounces) then
	 * 			|	super.bounceOffBoundary();
	 * @effect	...
	 * 			|if(!checkNubOfBounces) then
	 * 			|	incrementNbOfBounces();
	 * 			
	 */
	@Override
	public void bounceOffBoundary(){ 
		if(!checkNbOfBounces()){
			super.bounceOffBoundary();
			this.incrementNbOfBounces();
		}
		else 
			this.destroy();
	}
	
	
}
